package net.asg.games.yokeltowers.models;

import net.asg.games.yokeltowers.LevelManager;
import net.asg.games.yokeltowers.view.BlockYokelGameObject;
import net.asg.games.yokeltowers.view.BlockYokelGameObject.BlockType;
import net.asg.games.yokeltowers.view.BrokenBlockYokelGameObject;
import net.asg.games.yokeltowers.view.ColumnYokelGameObject;
import net.asg.games.yokeltowers.view.ThreeColumnYokelGameObject;
import net.asg.games.yokeltowers.view.YokelGameObject;
import net.asg.games.yokeltowers.view.YokelGameObjectFactory;

import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.utils.Array;

public class YokelGameBoard {
	//public enum BoardState { FALLING, SET, SETTING, TOUCHED_DOWN }
	public final static int BOARD_LENGTH = 6;
	public final static int BOARD_HEIGHT = 17;
	public final static int BOARD_ROW_LIMIT = 14;
	public final static int BOARDBOX_X = 322;
	public final static int BOARDBOX_Y = 130;
	public final static int BOARDBOX_WIDTH = BOARD_LENGTH * 16;
	public final static int BOARDBOX_HEIGHT = BOARD_HEIGHT * 16;
	
	public final static int BROKEN_Y_LIMIT = 8;
	public final static int BROKEN_O_LIMIT = 6;
	public final static int BROKEN_K_LIMIT = 6;
	public final static int BROKEN_E_LIMIT = 7;
	public final static int BROKEN_L_LIMIT = 6;
	public final static int BROKEN_BASH_LIMIT = 6;
	
	public final static int P1_NEXT_BOX_X = 322; //322
	public final static int P1_NEXT_BOX_Y = 206; //306
	public final static int START_COLUMN = 2;
	public final static int P1_START_Y = 356;
	public final static int P1_FLOOR = 130;
	public final static int P1_LEFT_WALL = 341;
	public final static int P1_RIGHT_WALL = 442;
	public final static int P2_NEXT_BOX_X = 322; //322
	public final static int P2_NEXT_BOX_Y = 306; //306
	public final static int P2_START_Y = 356;
	public final static int P2_FLOOR = 130;
	public final static int P2_LEFT_WALL = 341;
	
	protected int playerID;
	protected int boardID;
	
	public boolean inactive;

	public int brokenYblocks;
	public int brokenOblocks;
	public int brokenKblocks;
	public int brokenEblocks;
	public int brokenLblocks;
	public int brokenBashblocks;
	
	public Rectangle gameboardBounds;
	public Rectangle fallboxBounds;
	public Rectangle nextboxBounds;
	public Rectangle specialColumnBounds;

	
	protected Array<BlockYokelGameObject> playerBoard;
	protected Array<Integer> columnCount;
	protected YokelGameObjectFactory yokelGameObjectFactory;
	
	private ThreeColumnYokelGameObject nextColumn;
	private YokelGameObjectContainer specialColumn;
	private boolean isOddPlayer;
	private int nextBoxX;
	private int nextBoxY;

	public YokelGameBoard(YokelGameObjectFactory yokelGameObjectFactory, int playerIndex){
		playerBoard = new Array<BlockYokelGameObject>();
		columnCount = new Array<Integer>();
		this.yokelGameObjectFactory = yokelGameObjectFactory;
		clearBoard(playerBoard);
		for(int x = 0;x < BOARD_LENGTH; x++){
			columnCount.add(0);
		}

		brokenYblocks = 0;
		brokenOblocks = 0;
		brokenKblocks = 0;
		brokenEblocks = 0;
		brokenLblocks = 0;
		brokenBashblocks = 0;
		
		nextColumn = yokelGameObjectFactory.getRandomColumn();
		//nextColumn.setX(P1_NEXT_BOX_X);
		//nextColumn.setY(P1_NEXT_BOX_Y);
		
		specialColumn = new YokelGameObjectContainer(322, 100, nextColumn.rect.width, nextColumn.rect.height * 3);
		
		isOddPlayer = (playerIndex % 2 == 0 ? false : true);
		nextBoxX = (isOddPlayer ? P1_NEXT_BOX_X : P2_NEXT_BOX_X);
		nextBoxY = (isOddPlayer ? P1_NEXT_BOX_Y : P2_NEXT_BOX_Y);
		
		gameboardBounds = new Rectangle();
		gameboardBounds.x = BOARDBOX_X;
		gameboardBounds.y = BOARDBOX_Y;
		gameboardBounds.width = nextColumn.rect.width * BOARD_LENGTH + 4 + nextColumn.rect.width;
		gameboardBounds.height = nextColumn.rect.width * BOARD_HEIGHT;
		
		fallboxBounds = new Rectangle();
		fallboxBounds.x = gameboardBounds.x + 4 + nextColumn.rect.width;
		fallboxBounds.y = gameboardBounds.y;
		fallboxBounds.width = nextColumn.rect.width * BOARD_LENGTH;
		fallboxBounds.height = nextColumn.rect.width * BOARD_ROW_LIMIT;	
		
		nextboxBounds = new Rectangle();
		nextboxBounds.x = gameboardBounds.x;
		nextboxBounds.y = fallboxBounds.y + (fallboxBounds.height - nextColumn.rect.height);
		nextboxBounds.width = nextColumn.rect.width;
		nextboxBounds.height = nextColumn.rect.height;
		
		specialColumnBounds = new Rectangle();
		specialColumnBounds.x = gameboardBounds.x;
		specialColumnBounds.y = gameboardBounds.y;
		specialColumnBounds.width = nextColumn.rect.width;
		specialColumnBounds.height = nextColumn.rect.height * 3;
		
		nextColumn.setX((int) nextboxBounds.x);
		nextColumn.setY((int) nextboxBounds.y);
	}
	
	public ThreeColumnYokelGameObject resetCurrentColumn(LevelManager levelManager){
		ThreeColumnYokelGameObject tempObj = nextColumn;
		//System.out.println(nextColumn.rect.width);
		tempObj.setX((int) (fallboxBounds.x + START_COLUMN * nextColumn.rect.width));
		tempObj.setY(P1_START_Y);
		this.nextColumn = (ThreeColumnYokelGameObject) levelManager.getNextObject(yokelGameObjectFactory);
		return tempObj;
	}

	public void putBlock(Rectangle bounds, BlockYokelGameObject block){
		int col = getCol(bounds,block);
		int row = getRow(bounds,block);

		block.rect.setY(block.rect.width * row + bounds.y);
		playerBoard.set(row * BOARD_LENGTH + col, block);
	}
	
	public ThreeColumnYokelGameObject getNextColumn(){
		return this.nextColumn;
	}

	public BlockYokelGameObject getBlock(int row, int col){
		return playerBoard.get(row * BOARD_LENGTH + col);
	}

	private int getCol(Rectangle bounds, BlockYokelGameObject block){
		return (int) ((((block.rect.x >= bounds.x) ? block.rect.x : bounds.x) - bounds.x) / block.rect.width);
	}

	private int getRow(Rectangle bounds, BlockYokelGameObject block){
		return Math.round(((((block.rect.y < bounds.y) ? bounds.y : block.rect.y) - bounds.y) / block.rect.height));
	}

	public void removeBlock(int row, int col){
		playerBoard.set(row * BOARD_LENGTH + col, yokelGameObjectFactory.getClearBlock());
	}

	public void deleteRow(int row){

	}

	public void addRow(int row){

	}

	public int getColumnCount(int row){
		return columnCount.get(row);
	}

	public void sendGameOver(){

	}

	public void clearBoard(Array<BlockYokelGameObject> playerBoard){
		if (playerBoard != null)
			playerBoard.clear();
		for(int i = 0; i < BOARD_LENGTH * BOARD_HEIGHT; i++){
			playerBoard.add(yokelGameObjectFactory.getClearBlock());
		}
	}

	public void renderBoard(SpriteBatch batch, float delta){
		int row = 0;
		int col = 0;

		for(int i = 0; i < BOARD_LENGTH * BOARD_HEIGHT; i++){	
			playerBoard.get(row * BOARD_LENGTH + col).draw(batch, delta);

			if (col < BOARD_LENGTH - 1){
				col++;
			} else {
				col = 0;
				row++;
			}
		}
		
		nextColumn.setX((int) nextboxBounds.x);
		nextColumn.setY((int) nextboxBounds.y);

		nextColumn.draw(batch, delta);
	}

	public void renderBoardDebug(ShapeRenderer debugRenderer){
		int row = 0;
		int col = 0;

		for(int i = 0; i < BOARD_LENGTH * BOARD_HEIGHT; i++){		
			playerBoard.get(row * BOARD_LENGTH + col).drawDebug(debugRenderer);

			if (col < BOARD_LENGTH - 1){
				col++;
			} else {
				col = 0;
				row++;
			}
		}
		
		nextColumn.drawDebug(debugRenderer);
		debugRenderer.rect(gameboardBounds.x, gameboardBounds.y, gameboardBounds.width, gameboardBounds.height);
		debugRenderer.rect(fallboxBounds.x, fallboxBounds.y, fallboxBounds.width, fallboxBounds.height);
		debugRenderer.rect(nextboxBounds.x, nextboxBounds.y, nextboxBounds.width, nextboxBounds.height);
		debugRenderer.rect(specialColumnBounds.x, specialColumnBounds.y, specialColumnBounds.width, specialColumnBounds.height);
	}

	public String toString(){
		int row = 0;
		int col = 0;
		String board = "";

		for(int i = 0; i < BOARD_LENGTH * BOARD_HEIGHT; i++){		
			board += playerBoard.get(row * BOARD_LENGTH + col) + "";
			if (col < BOARD_LENGTH - 1){
				col++;
			} else {
				col = 0;
				row++;
				board += "\n";
			}
		}
		return board;
	}


	public boolean detectCollision(Rectangle bounds, BlockYokelGameObject block){
		int col = getCol(bounds,block);
		int row = getRow(bounds,block);

		if (block.rect.y <= bounds.y)
			return true;

		BlockYokelGameObject block2 = block;

		if(row > 0){
			block2 = playerBoard.get((row-1) * BOARD_LENGTH + col);
			if (block.rect.y < (block2.rect.y + block.rect.height) && !block2.getBlockType().equals(BlockType.CLEAR_BLOCK))
				return true;
		}

		return false;
	}

	public void drawClip(Rectangle bounds, ColumnYokelGameObject block, ShapeRenderer debugRenderer){
		int col = getCol(bounds,block.getBlock(0));
		int row = getRow(bounds,block.getBlock(0));

		Rectangle localBounds = new Rectangle(bounds.x,bounds.y,bounds.width,bounds.height);

		if (col - 1 > -1){
			BlockYokelGameObject block1 = playerBoard.get((row) * BOARD_LENGTH + (col - 1));
			if(block1.getBlockType() != BlockType.CLEAR_BLOCK)
				localBounds.setX(block.rect.x);
		}

		if (col < BOARD_LENGTH + 1){
			BlockYokelGameObject block1 = playerBoard.get((row) * BOARD_LENGTH + (col + 1));
			if(block1.getBlockType() != BlockType.CLEAR_BLOCK)
				localBounds.setWidth(block.rect.x - localBounds.x + block.rect.width);
		}
		
		if (row <= 0){
			BlockYokelGameObject block1 = playerBoard.get((row) * BOARD_LENGTH + (col));
			if(block1.getBlockType() != BlockType.CLEAR_BLOCK)
				localBounds.setY(bounds.y+(block.rect.height * (row)));
	}
		
		debugRenderer.rect(localBounds.x, localBounds.y, localBounds.width, localBounds.height);
	}

	public void doClip(Rectangle bounds, ColumnYokelGameObject block, int inKeys) {
		int col = getCol(bounds,block.getBlock(0));
		int row = getRow(bounds,block.getBlock(0));		
		
		Rectangle localBounds = new Rectangle(bounds.x,bounds.y,bounds.width,bounds.height);
		if (inKeys == Keys.LEFT){
				BlockYokelGameObject block1 = playerBoard.get((row) * BOARD_LENGTH + (col));
				if(block1.getBlockType() != BlockType.CLEAR_BLOCK)
					localBounds.setX(bounds.x+(block.rect.width * (col+1)));
		}
		
		if (inKeys == Keys.RIGHT){
				BlockYokelGameObject block1 = playerBoard.get((row) * BOARD_LENGTH + (col));
				if(block1.getBlockType() != BlockType.CLEAR_BLOCK)
					localBounds.setWidth(block.rect.width * (col));
		}
		
		if (inKeys == Keys.DOWN){
				BlockYokelGameObject block1 = playerBoard.get((row) * BOARD_LENGTH + (col));
				if(block1.getBlockType() != BlockType.CLEAR_BLOCK)
					localBounds.setY(bounds.y+(block.rect.height * (row)));
		}

		block.keepInBounds(localBounds);
	}

	private Array<BrokenBlockYokelGameObject> getBrokenBlockArray(BlockYokelGameObject block1, BlockYokelGameObject block2, BlockYokelGameObject block3){
		Array<BrokenBlockYokelGameObject> brokenBlockArray = new Array<BrokenBlockYokelGameObject>();

		BrokenBlockYokelGameObject brokenblock1 = yokelGameObjectFactory.getBrokenBlock(block1.blockType);
		BrokenBlockYokelGameObject brokenblock2 = yokelGameObjectFactory.getBrokenBlock(block2.blockType);
		BrokenBlockYokelGameObject brokenblock3 = yokelGameObjectFactory.getBrokenBlock(block3.blockType);

		brokenblock1.setX(block1.rect.x);
		brokenblock1.setY(block1.rect.y);
		brokenblock2.setX(block2.rect.x);
		brokenblock2.setY(block2.rect.y);
		brokenblock3.setX(block3.rect.x);
		brokenblock3.setY(block3.rect.y);

		brokenBlockArray.add(brokenblock1);
		brokenBlockArray.add(brokenblock2);
		brokenBlockArray.add(brokenblock3);

		return brokenBlockArray;
	}


	private boolean isBlocksEqual(BlockYokelGameObject block1, BlockYokelGameObject block2, BlockYokelGameObject block3){
		return (block1.equals(block2) && block1.equals(block3));
	}

	public void checkMatches(Rectangle bounds){
		int col = 0;
		int row = 0;

		while (row < BOARD_ROW_LIMIT - 2){
			BlockYokelGameObject block1 = playerBoard.get((row) * BOARD_LENGTH + (col));
			BlockYokelGameObject block2 = playerBoard.get((row) * BOARD_LENGTH + (col + 1));
			BlockYokelGameObject block3 = playerBoard.get((row) * BOARD_LENGTH + (col + 2));
			BlockYokelGameObject block4 = playerBoard.get((row + 1) * BOARD_LENGTH + (col));
			BlockYokelGameObject block5 = playerBoard.get((row + 1) * BOARD_LENGTH + (col + 1));
			BlockYokelGameObject block6 = playerBoard.get((row + 1) * BOARD_LENGTH + (col + 2));
			BlockYokelGameObject block7 = playerBoard.get((row + 2) * BOARD_LENGTH + (col));
			BlockYokelGameObject block8 = playerBoard.get((row + 2) * BOARD_LENGTH + (col + 1));
			BlockYokelGameObject block9 = playerBoard.get((row + 2) * BOARD_LENGTH + (col + 2));

			//HORIZONTAL YOKEL!
			if(col == 3 && checkEL_(block1,block2,block3)){
				BlockYokelGameObject kblock = playerBoard.get((row) * BOARD_LENGTH + (col-1));
				BlockYokelGameObject oblock = playerBoard.get((row) * BOARD_LENGTH + (col-2));
				BlockYokelGameObject yblock = playerBoard.get((row) * BOARD_LENGTH + (col-3));
				if(kblock.getBlockType().equals(BlockType.K_BLOCK) && oblock.getBlockType().equals(BlockType.O_BLOCK) &&
						yblock.getBlockType().equals(BlockType.Y_BLOCK)){
					if(checkYOKEL_(yblock,oblock,kblock,block1,block2,block3))
						System.out.println("HORIZONTAL YOKEL!");
				}
			}

			//DIAGONAL UP YOKEL!
			if(row > 2 && row < BOARD_ROW_LIMIT - 2){
				if(col == 3 && checkEL_(block1,block2,block3)){
					BlockYokelGameObject kblock = playerBoard.get((row - 1) * BOARD_LENGTH + (col-1));
					BlockYokelGameObject oblock = playerBoard.get((row - 2) * BOARD_LENGTH + (col-2));
					BlockYokelGameObject yblock = playerBoard.get((row - 3) * BOARD_LENGTH + (col-3));

					if(kblock.getBlockType().equals(BlockType.K_BLOCK) && oblock.getBlockType().equals(BlockType.O_BLOCK) &&
							yblock.getBlockType().equals(BlockType.Y_BLOCK)){
						if(checkYOKEL_(yblock,oblock,kblock,block1,block5,block9))
							System.out.println("DIAGONAL UP YOKEL!");
					}
				}
			}
			
			//DIAGONAL DOWN YOKEL!
			if(row < BOARD_ROW_LIMIT - 2){
				if(col == 3 && checkEL_(block7,block5,block3)){
					BlockYokelGameObject kblock = playerBoard.get((row + 3) * BOARD_LENGTH + (col-1));
					BlockYokelGameObject oblock = playerBoard.get((row + 4) * BOARD_LENGTH + (col-2));
					BlockYokelGameObject yblock = playerBoard.get((row + 5) * BOARD_LENGTH + (col-3));
					
					if(kblock.getBlockType().equals(BlockType.K_BLOCK) && oblock.getBlockType().equals(BlockType.O_BLOCK) &&
							yblock.getBlockType().equals(BlockType.Y_BLOCK)){
						if(checkYOKEL_(yblock,oblock,kblock,block7,block5,block3))
							System.out.println("DIAGONAL DOWN YOKEL!");
					}
				}
			}
			
			//VERTICAL YOKEL!
			if(row < BOARD_ROW_LIMIT - 2){
				if(checkEL_(block7,block4,block1)){
					BlockYokelGameObject kblock = playerBoard.get((row + 3) * BOARD_LENGTH + (col));
					BlockYokelGameObject oblock = playerBoard.get((row + 4) * BOARD_LENGTH + (col));
					BlockYokelGameObject yblock = playerBoard.get((row + 5) * BOARD_LENGTH + (col));
					
//					System.out.print(yblock+""+oblock+kblock+block7+block4+block1+"\n");
					
					if(kblock.getBlockType().equals(BlockType.K_BLOCK) && oblock.getBlockType().equals(BlockType.O_BLOCK) &&
							yblock.getBlockType().equals(BlockType.Y_BLOCK)){
						if(checkYOKEL_(yblock,oblock,kblock,block7,block4,block1))
							System.out.println("VERTICAL YOKEL!");
					}
				}
			}

			//check horizontal			
			if(isBlocksEqual(block1,block2,block3))
			{
				for (BlockYokelGameObject brokenBlock : getBrokenBlockArray(block1,block2,block3)){
					putBlock(bounds,brokenBlock);
				}
			}

			if(isBlocksEqual(block4,block5,block6))
			{
				for (BlockYokelGameObject brokenBlock : getBrokenBlockArray(block4,block5,block6)){
					putBlock(bounds,brokenBlock);
				}
			}

			if(isBlocksEqual(block7,block8,block9))
			{
				for (BlockYokelGameObject brokenBlock : getBrokenBlockArray(block7,block8,block9)){
					putBlock(bounds,brokenBlock);
				}
			}

			//check vertical
			if(isBlocksEqual(block1,block4,block7))
			{
				for (BlockYokelGameObject brokenBlock : getBrokenBlockArray(block1,block4,block7)){
					putBlock(bounds,brokenBlock);
				}
			}

			if(isBlocksEqual(block2,block5,block8))
			{
				for (BlockYokelGameObject brokenBlock : getBrokenBlockArray(block2,block5,block8)){
					putBlock(bounds,brokenBlock);
				}
			}

			if(isBlocksEqual(block3,block6,block9))
			{
				for (BlockYokelGameObject brokenBlock : getBrokenBlockArray(block3,block6,block9)){
					putBlock(bounds,brokenBlock);
				}
			}

			//check diagonal
			if(isBlocksEqual(block1,block5,block9))
			{
				for (BlockYokelGameObject brokenBlock : getBrokenBlockArray(block1,block5,block9)){
					putBlock(bounds,brokenBlock);
				}
			}

			if(isBlocksEqual(block7,block5,block3))
			{
				for (BlockYokelGameObject brokenBlock : getBrokenBlockArray(block7,block5,block3)){
					putBlock(bounds,brokenBlock);
				}
			}	

			col += 1;

			if (col > BOARD_LENGTH - 3){
				col = 0;
				row += 1;
			}					
		}
	}

	public boolean checkYOKEL_(BlockYokelGameObject block1, BlockYokelGameObject block2, BlockYokelGameObject block3,
			BlockYokelGameObject block4, BlockYokelGameObject block5, BlockYokelGameObject block6){
		return block1.getBlockType().equals(BlockType.Y_BLOCK) && block2.getBlockType().equals(BlockType.O_BLOCK) &&
				block3.getBlockType().equals(BlockType.K_BLOCK) && block4.getBlockType().equals(BlockType.E_BLOCK) &&
				block5.getBlockType().equals(BlockType.L_BLOCK) && block6.getBlockType().equals(BlockType.BASH_BLOCK);
	}
	
	public boolean checkEL_(BlockYokelGameObject block4, BlockYokelGameObject block5, BlockYokelGameObject block6){
		return block4.getBlockType().equals(BlockType.E_BLOCK) &&
				block5.getBlockType().equals(BlockType.L_BLOCK) && block6.getBlockType().equals(BlockType.BASH_BLOCK);
	}

	public void setColumn(Array<BlockYokelGameObject> column, Rectangle bounds){
		for (BlockYokelGameObject block : column){
			putBlock(bounds, block);
		}
		
	}

	public void getBrokenObjects(Array<YokelGameObject> fallingObjects, Rectangle bounds) {
		for (int i = 0; i < BOARD_LENGTH * BOARD_HEIGHT; i++){
			BlockYokelGameObject block = playerBoard.get(i);
			if (block instanceof BrokenBlockYokelGameObject){
				fallingObjects.add(block);
				removeBlock(getRow(bounds,block),getCol(bounds,block));
				fallingObjects.add(buildColumn(getRow(bounds,block),getCol(bounds,block),bounds));
			}
		}
	}

	private ColumnYokelGameObject buildColumn(int row, int col, Rectangle bounds){
		int currow = row;
		ColumnYokelGameObject column = yokelGameObjectFactory.getColumn();
		while (currow < BOARD_ROW_LIMIT){
			if(!getBlock(currow,col).blockType.equals(BlockType.CLEAR_BLOCK)){
				column.addBlock(getBlock(currow,col));
				removeBlock(currow,col);
			}
			currow++;
		}

		return column;
	}

	public Rectangle getGamebounds() {
		return fallboxBounds;
	}
}
